assume cs:code, ds:data, ss:stack

data segment
    db '1975', '1976', '1977', '1978', '1979', '1980', '1981', '1982', '1983'
    db '1984', '1985', '1986', '1987', '1988', '1989', '1990', '1991', '1992'
    db '1993', '1994', '1995'
    ; 以上表示 21 年的 21 个字符串

    dd 16, 22, 382, 1356, 2390, 8000, 16000, 24486, 50065, 97479, 140417, 197514
    dd 345980, 590827, 803530, 1183000, 1843000, 2759000, 3753000, 4649000, 5937000
    ; 以上是表示 21 年公司收入的 21 个 dword 型数据

    dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001, 1442, 2258, 2793, 4037, 5635, 8226
    dw 11542, 14430, 15257, 17800
    ; 以上是表示 21 年公司雇员人数的 21 个 word 型数据
data ends

stack segment
    dw 10 dup(0)
stack ends

table segment
    db 21 dup ('year summ ne ?? ')
table ends

; 定义这个段记住 data 段中三种数据的当前地址
point segment
    dw 00h, 54h, 00a8h
point ends

code segment
    start:
        ; 堆栈段准备
        mov ax, stack
        mov ss, ax
        mov sp, 14h

        ; 设置计数器
        mov cx, 15h
        mov bx, 00h
        s:
            ; 年份
            ; 操作数据指针指向对应年份数据
            mov ax, point
            mov ds, ax
            mov si, ds:[0]
            ; 外层循环变量压栈暂存
            push cx
            ; 设置内层循环变量
            mov cx, 02h
            s0:
                ; 获取年份低字节压入栈中暂存
                mov ax, data
                mov ds, ax
                push [si]
                add si, 02h ; 数据指针后移
                ; 操作修改指针指向对应的修改年份的位置
                mov ax, table
                mov ds, ax
                mov di, 00h
                ; 从栈中弹出并赋值给目标地址
                pop [bx].0[di]
                add di, 02h ; 操作指针后移
                loop s0
            mov byte ptr [bx].0[di], ' '
            ; 暂存年份数据指针
            mov ax, point
            mov ds, ax
            mov ds:[0], si

            ; 收入
            ; 操作数据指针指向对应收入数据
            mov ax, point
            mov ds, ax
            mov si, ds:[2]
            ; 设置内层循环变量
            mov cx, 02h
            s1:
                ; 获取收入低字节压入栈中暂存
                mov ax, data
                mov ds, ax
                push [si]
                add si, 02h ; 数据指针后移
                ; 操作修改指针指向对应的修改收入的位置
                mov ax, table
                mov ds, ax
                mov di, 05h
                ; 从栈中弹出并赋值给目标地址
                pop [bx].0[di]
                add di, 02h ; 操作指针后移
                loop s1
            mov byte ptr [bx].0[di], ' '
            ; 暂存收入数据指针
            mov ax, point
            mov ds, ax
            mov ds:[2], si

            ; 雇员数
            ; 操作数据指针指向对应雇员数数据
            mov ax, point
            mov ds, ax
            mov si, ds:[4]
            ; 获取雇员数压入栈中暂存
            mov ax, data
            mov ds, ax
            push [si]
            add si, 02h ; 数据指针后移
            ; 操作修改指针指向对应的修改雇员数的位置
            mov ax, table
            mov ds, ax
            mov di, 0ah
            ; 从栈中弹出并赋值给目标地址
            pop [bx].0[di]
            add di, 02h ; 操作指针后移
            mov byte ptr [bx].0[di], ' '
            ; 暂存雇员数数据指针
            mov ax, point
            mov ds, ax
            mov ds:[4], si

            ; 人均收入
            ; 操作读写指针指向对应的总收入和雇员数的位置
            mov ax, table
            mov ds, ax
            mov ax, [bx].05h
            mov dx, [bx].07h
            div word ptr [bx].0ah
            mov [bx].0dh, ax
            ; 空格
            mov byte ptr [bx].0fh, ' '
            ; 转到下一行
            add bx, 10h
            pop cx
            loop s

        mov ax, 4100h
        int 21h
code ends
end start
; 这个在想法上是可以的，但是实现上失败了，因为loop不能跳转太远